<template>
    <div v-show="!_show_details_card" class="grid-panel">
        <div class="grid-toolbar">
            <el-tag class="tool-tag" type="info" effect="dark" size="default"># {{ _machine }}</el-tag>
            <el-tag class="tool-tag" type="info" effect="dark" size="default">{{ _dataset_info &&
        _dataset_info.label_format }}</el-tag>
            <el-icon class="close-btn" :size="16" @click.stop="closeGrid()">
                <CloseBold />
            </el-icon>
        </div>
        <div id="grid_wrapper" class="grid-wrapper" @scroll="handleScroll">
            <div class="grid-header"></div>
            <div v-for="(row, rindex) in _grid_visible" :key="row.row_index" class="grid-row">
                <div v-for="(cell, cindex) in _grid_visible[rindex].grids"
                    :style="{ left: cell.left + 'px', top: cell.top + 'px', width: cell.width + 'px', height: cell.height + 'px' }"
                    :key="cell.pairs_id" class="grid-cell-wrapper" @click="onClickCell(cell, row.row_index, cindex)">
                    <div class="grid-cell">
                        <LabelThumb :_dataset_info="_dataset_info" :_rindex="row.row_index" :_cindex="cindex"
                            :_pairs_id="cell.pairs_id" :_image_url="cell.image_url" :_label_url="cell.label_url"
                            :_loading_status="_grid_loading_status[cell.pairs_id]" :_loading_remote="true"
                            @image-loaded="onImageLoaded"></LabelThumb>
                    </div>
                </div>
            </div>
            <div class="grid-footer" :style="{ top: _grid_footer.top + 'px', width: _grid_footer.width + 'px' }"></div>
        </div>
    </div>
    <div v-show="_show_details_card" class="label-details-card" shadow="always">
        <div class="image-region">
            <ImageRegion :_region_id="`preview`" :_readonly="true" :_toolbar="`none`" :_sync_ops="false"
                :_show_name="true" :_highlight="true" ref="image_region"></ImageRegion>
        </div>
        <el-icon class="label-card-close-btn" :size="16" @click="closePreview()">
            <CloseBold />
        </el-icon>
    </div>
</template>

<script setup>
import isEqual from 'lodash/isEqual';
import { throttle } from '@/js/utils/baseUtils';
import { createLabel } from '@/js/models/LabelBase.js';
import { ref, onMounted, onUnmounted, getCurrentInstance, inject, nextTick } from 'vue';

import API from '@/js/api';
import LabelConfig from '@/js/models/LabelConfig';
import LabelStorageLocal from '@/js/models/LabelStorageLocal';
import { getFileBaseName } from '@/js/utils/fileUtils';
import LabelThumb from './LabelThumb.vue';
import ImageRegion from '@/components/ToolPages/ImageRegion.vue';

const name = 'GridPanel';
let vueApp = inject('vueApp');
const { proxy } = getCurrentInstance();
const emit = defineEmits(['selectPanel']);

const _ratio_threshold = [5, 4, 3, 2];
const methods = {
    preLabelData: preLabelData,
    nextLabelData: nextLabelData,
    closePreview: closePreview
};

// 数据相关的成员变量
let _machine = ref('Local');
let _dataset_id = ref('');
let _dataset_info = ref({});
let _current_page = ref(1);

// 布局/显示相关的成员变量
let _grid_viewport = {};
let _grid_all = ref([]);
let _grid_visible = ref([]);
let _grid_footer = ref({});
let _update_layout = false;

// grid索引数据
let _grid_index = [];
let _grid_block_size = 2000; // 构建索引的高度（单位：像素，尽可能超出窗口高度x2？）
let _grid_page_size = 100; // 加载数据的pagesize

// 定义单独的加载状态的标识位
let _grid_loading_status = ref({});

// 显示图片详情的参数
let _show_details_card = ref(false);

// 选择和切换cell相关的参数
let _rindex = null;
let _cindex = null;

async function loadDataset(dataset_id) {
    // console.log('GridPanel loadDataset dataset_id = ', dataset_id);
    _dataset_id.value = dataset_id;
    const loading = ElLoading.service({
        lock: true,
        text: '数据集加载中...',
        background: 'rgba(0, 0, 0, 0.7)'
    });

    // 获取dataset的基础信息
    let result = await API.invoke('GetDatasetInfo', { dataset_id: dataset_id });
    if (result.code === 0) {
        _machine.value = API.getProvider();

        let dataset_info = result.msg;
        _dataset_info.value = dataset_info;

        // 从本地加载dataset信息
        let page_info = await LabelStorageLocal.loadDataset(dataset_info, {
            load_details: true,
            page_size: _grid_page_size
        });
        loading.close();

        // 当前页做grid布局
        _current_page.value = 1;
        updateGridLayout(_grid_all.value, page_info.labeldatas, _grid_viewport);
        // console.log(_grid_all.value);

        // image_region加载dataset信息
        proxy.$refs.image_region.loadDataset(dataset_info);
    } else {
        console.error('GetDatasetInfo Failed!');
        loading.close();
    }
}

// 添加数据源、更新可视窗口后更新layout数据
function updateGridLayout(grids, page_data, viewport, tindex = 0) {
    // console.log('updateGridLayout grids = ', grids);
    // console.log('updateGridLayout viewport = ', viewport);
    let grid = []; // grid结构保存对齐height后的cell列表（可以想象成一整行）
    let row = [];
    let rowWidth = 0;
    let rowWidthList = [];
    let left = 0;
    let top = 0;
    let image_list = [];

    if (page_data) {
        // 当有新增数据时候，构建grid数据结构
        // 转换page数据到image_list
        image_list = page_data
            .filter((item) => item.image_info.width > 0 && item.image_info.height > 0)
            .map((item) => ({
                pairs_id: item.pairs_id,
                image_url: item.image_url,
                label_url: item.label_url,
                width: item.image_info.width,
                height: item.image_info.height
            }));

        // 判断grids最后一行是否完整
        let last_row_index = grids.length - 1;
        let last_row = null;
        if (last_row_index >= 0) {
            last_row = grids[last_row_index];
            top = last_row.row_bottom;
        }

        // console.log('last_row = ', last_row && last_row.grids);
        if (last_row && last_row.ratio_width < _ratio_threshold[tindex]) {
            for (let i = last_row.grids.length - 1; i >= 0; i--) {
                image_list.unshift({
                    pairs_id: last_row.grids[i].pairs_id,
                    image_url: last_row.grids[i].image_url,
                    label_url: last_row.grids[i].label_url,
                    width: last_row.grids[i].width,
                    height: last_row.grids[i].height
                });
            }
            grids.splice(last_row_index, 1);
            last_row = grids[last_row_index - 1];
            top = last_row.row_bottom;
        }
    } else {
        // 没有新增数据，只是view变化导致的数据更新
        for (let i = 0; i < grids.length; i++) {
            for (let j = 0; j < grids[i].grids.length; j++) {
                let item = grids[i].grids[j];
                image_list.push({
                    pairs_id: item.pairs_id,
                    image_url: item.image_url,
                    label_url: item.label_url,
                    width: item.width,
                    height: item.height
                });
            }
        }
        grids = [];
    }

    // 固定height计算ratio，根据阈值进行分行处理
    for (let i = 0; i < image_list.length; i++) {
        let image_info = image_list[i];
        image_info.ratio = image_info.width / image_info.height;
        row.push(image_info);
        rowWidth += image_info.ratio;
        if (rowWidth >= _ratio_threshold[tindex]) {
            rowWidthList.push(rowWidth);
            grid.push(row);
            row = [];
            rowWidth = 0;
        }
    }
    if (row.length > 0) {
        rowWidthList.push(rowWidth);
        grid.push(row);
        row = [];
        rowWidth = 0;
    }

    // 判断grid不能为空
    if (!grid || grid.length == 0) {
        // console.log(`GridPanel updateGridLayout grid is null!`);
        return;
    }
    // console.log(grid);

    // 对于分行的数据，重新计算每行高度，输出最终的grid尺寸
    let new_grids = [];
    let start_paint = false;
    for (let i = 0; i < grid.length; i++) {
        // 设置每行的信息
        let row = grid[i];
        let rowWidth = rowWidthList[i];
        let rowHeight = viewport.width / rowWidth;
        if (rowWidth < _ratio_threshold[tindex]) {
            rowHeight = viewport.width / _ratio_threshold[tindex];
        }
        new_grids[i] = {
            row_top: top,
            row_bottom: top + rowHeight,
            row_height: rowHeight,
            row_width: viewport.width,
            ratio_width: rowWidth,
            grids: []
        };

        // 设置grid中每一个cell的坐标
        left = 0;
        for (let j = 0; j < row.length; j++) {
            let gridWidth = rowHeight * row[j].ratio;
            new_grids[i].grids[j] = {
                left: left,
                top: top,
                width: gridWidth,
                height: rowHeight,
                pairs_id: row[j].pairs_id,
                image_url: row[j].image_url,
                label_url: row[j].label_url
            };
            left += gridWidth;

            // 设置cell的图片加载状态
            if (!start_paint) {
                let tmp_pairs_id = row[j].pairs_id;
                if (!_grid_loading_status.value[tmp_pairs_id]) {
                    // console.warn(`row[j].pairs_id = ${tmp_pairs_id}`);
                    _grid_loading_status.value[tmp_pairs_id] = true;
                    start_paint = true;
                }
            } else {
                _grid_loading_status.value[row[j].pairs_id] = false;
            }
        }
        top += rowHeight;
    }

    // 合并最后的结果，并构建结果和筛选数据
    grids.push(...new_grids);
    buildGridIndex(grids);
    selectVisibleRows();

    // 设定最后一行的位置，确保滚动不会出现问题
    let root_row = new_grids[new_grids.length - 1];
    _grid_footer.value = {
        top: root_row.row_bottom,
        width: root_row.row_width
    };
    // console.log(grids);
}

async function nextPageData() {
    if (_update_layout) {
        return;
    }
    // console.log('开始更新grid layout数据......');
    _update_layout = true;
    let page_data = await LabelStorageLocal.getPageData(_current_page.value + 1);
    if (page_data.length > 0) {
        // console.log(`加载并更新第${_current_page.value + 1}页数据, page_data = `, page_data);
        updateGridLayout(_grid_all.value, page_data, _grid_viewport);
        _current_page.value = _current_page.value + 1;
    }
    // console.log('更新完成grid layout数据, _current_page = ', _current_page.value);
    _update_layout = false;
}

// 创建grid对象的索引结构，grid_rows是要创建索引的数据，block_size是索引块的大小
function buildGridIndex(grid_rows) {
    _grid_index = [];
    for (let i = 0; i < grid_rows.length; i++) {
        let grid_row = grid_rows[i];
        grid_row.row_index = i; //增加一个表示
        let start_index = parseInt(grid_row.row_top / _grid_block_size);
        let end_index = parseInt(grid_row.row_bottom / _grid_block_size);
        for (let j = start_index; j <= end_index; j++) {
            if (_grid_index[j]) {
                _grid_index[j].push(grid_row);
            } else {
                _grid_index[j] = [grid_row];
            }
        }
    }
    // console.log(block_index);
}

// 从所有的grid数据中，判断哪些是可见的
function selectVisibleRows() {
    if (_update_layout) {
        return;
    }
    // console.log('===selectVisibleRows===');
    _update_layout = true;
    const visibleRows = [];
    const visibleRowsSet = new Set();

    let start_index = parseInt(_grid_viewport.top / _grid_block_size);
    let end_index = parseInt(_grid_viewport.bottom / _grid_block_size);
    // console.log(`start_index = ${start_index}, end_index = ${end_index}`);
    for (let i = start_index; i <= end_index; i++) {
        let rows = _grid_index[i] || [];
        for (let j = 0; j < rows.length; j++) {
            let row = rows[j];

            // 如果Set中已存在当前行对象的引用，则跳过，以避免重复
            if (visibleRowsSet.has(row.row_index)) continue;
            // console.log(row);

            // 根据视口调整显示的行数，当前保证不闪烁，所以使用了覆盖block的所有行
            visibleRows.push(row);
            visibleRowsSet.add(row.row_index);
            // if (!(row.row_bottom < _grid_viewport.top || row.row_top > _grid_viewport.bottom)) {
            //     visibleRows.push(row);
            //     visibleRowsSet.add(row);
            // }
        }
    }

    let old_row_indexs = _grid_visible.value.map((item) => {
        return {
            row_index: item.row_index,
            row_top: item.row_top,
            row_bottom: item.row_bottom,
            row_width: item.row_width,
            row_height: item.row_height
        };
    });
    let new_row_indexs = visibleRows.map((item) => {
        return {
            row_index: item.row_index,
            row_top: item.row_top,
            row_bottom: item.row_bottom,
            row_width: item.row_width,
            row_height: item.row_height
        };
    });
    // console.log(`old_row_indexs = `, old_row_indexs);
    // console.log(`new_row_indexs = `, new_row_indexs);
    if (!isEqual(old_row_indexs, new_row_indexs)) {
        // console.warn(`## update _grid_visible value, display rows = `, visibleRows.length);
        _grid_visible.value = visibleRows;
    }

    _update_layout = false;
}

async function showCellPreview(cell) {
    // 加载label数据
    let algo_type = vueApp.getAlgoType();
    let label_result = await createLabel(algo_type, _dataset_info.value.label_format);
    let result = await API.invoke('LoadLabel', {
        dataset_id: _dataset_info.value.id,
        dataset_supertype: algo_type.supertype,
        dataset_type: _dataset_info.value.dataset_type,
        label_format: _dataset_info.value.label_format,
        image_path: cell.image_url,
        label_path: cell.label_url
    });
    // console.log(result);
    if (result.code == 0) {
        label_result.fromJson(result.msg);
        let labeldata = {
            pairs_id: cell.pairs_id,
            image_url: cell.image_url,
            label_url: cell.label_url,
            name: getFileBaseName(cell.image_url),
            label_result: label_result
        };

        // 打开展示
        if (_show_details_card.value == false) {
            _show_details_card.value = true;
            await nextTick();
        }

        // image_region加载数据
        proxy.$refs.image_region.loadData(labeldata);
    } else {
        console.error('加载label数据失败！');
    }
}

function onImageLoaded(cell_info) {
    // console.log('onImageLoaded cell=', cell_info);
    let { rindex, cindex, pairs_id, loaded } = cell_info;

    // 获取下一个加载图片的位置
    let row = _grid_all.value[rindex];
    if (cindex == row.grids.length - 1) {
        if (rindex + 1 == _grid_all.value.length) {
            return;
        }

        rindex += 1;
        cindex = 0;
    } else {
        cindex += 1;
    }

    // 获取对应下一个位置的pairs_id
    // console.log(`onImageLoaded rindex = ${rindex}, cindex = ${cindex}, pairs_id=${pairs_id}`);
    row = _grid_all.value[rindex];
    let cell = row.grids[cindex];
    if (cell) {
        let pairs_id = cell.pairs_id;
        _grid_loading_status.value[pairs_id] = true;
    } else {
        console.warn(`no cell found, rindex = ${rindex}, cindex = ${cindex}`);
    }
}

function onClickCell(cell, rindex, cindex) {
    // console.log(`onClickCell rindex = ${rindex}, cindex = ${cindex}`);
    // console.log('onClickCell cell = ', cell);
    _rindex = rindex;
    _cindex = cindex;
    showCellPreview(cell);
}

function handleKeyDown(event) {
    // console.log('GridPanel::handleKeyDown event = ', event);
    LabelConfig.runShortCuts('GridPanel', methods, event);
}

function preLabelData() {
    // console.log('GridPanel preLabelData.');
    if (_rindex == null || _cindex == null) {
        return false;
    }
    let row = null;
    if (_cindex == 0) {
        if (_rindex == 0) {
            return false;
        }
        _rindex -= 1;
        row = _grid_all.value[_rindex];
        _cindex = row.grids.length - 1;
    } else {
        _cindex -= 1;
    }

    // 获取并展示对应的cell数据
    row = _grid_all.value[_rindex];
    let cell = row.grids[_cindex];
    if (cell) {
        showCellPreview(cell);
    }
}

function nextLabelData() {
    // console.log('GridPanel nextLabelData.');
    if (_rindex == null || _cindex == null) {
        return false;
    }
    let row = _grid_all.value[_rindex];
    if (_cindex == row.grids.length - 1) {
        if (_rindex + 1 == _grid_all.value.length) {
            return false;
        }

        _rindex += 1;
        _cindex = 0;
    } else {
        _cindex += 1;
    }

    // 获取并展示对应的cell数据
    row = _grid_all.value[_rindex];
    let cell = row.grids[_cindex];
    if (cell) {
        showCellPreview(cell);
    }
}

function closePreview() {
    _rindex = null;
    _cindex = null;
    _show_details_card.value = false;
}

function closeGrid() {
    emit('selectPanel', 'home_panel');
}

const handleScroll = throttle(async (event) => {
    const scrollTop = event.target.scrollTop;
    const scrollHeight = event.target.scrollHeight;
    const clientWidth = event.target.clientWidth;
    const clientHeight = event.target.clientHeight;

    // console.log(`scrollTop = ${scrollTop}\nclientHeight = ${clientHeight}\nscrollHeight = ${scrollHeight}`);
    _grid_viewport = {
        top: scrollTop,
        bottom: scrollTop + clientHeight,
        width: clientWidth,
        height: clientHeight
    };

    // 判断是否需要加载新数据
    if (scrollTop + clientHeight + 1.0 >= scrollHeight) {
        await nextPageData();
    }

    // 判断可见区域的grid
    selectVisibleRows();
}, 200);

function handleResize() {
    // console.log('>> GridPanel handleResize');
    let grid_panel = document.getElementById('grid_wrapper');
    _grid_viewport = {
        top: grid_panel.scrollTop,
        bottom: grid_panel.scrollTop + grid_panel.clientHeight,
        width: grid_panel.clientWidth,
        height: grid_panel.clientHeight
    };

    updateGridLayout(_grid_all.value, null, _grid_viewport);
}

onMounted(() => {
    handleResize();
    window.addEventListener('resize', handleResize);
});

onUnmounted(() => {
    window.removeEventListener('resize', handleResize);
});

defineExpose({ loadDataset, handleKeyDown });
</script>

<style scoped>
.grid-panel {
    width: 100%;
    height: 100%;
    overflow: hidden;
}

.grid-toolbar {
    position: relative;
    background-color: rgb(48, 46, 46);
    width: 100%;
    height: 40px;
}

.grid-wrapper {
    position: relative;
    background-color: rgb(186, 186, 186);
    width: 100%;
    height: calc(100% - 40px);
    overflow-y: scroll;
    overflow-x: hidden;
}

.grid-row {
    /* display: flex; */
    position: absolute;
    width: 0px;
    height: 0px;
}

.grid-cell-wrapper {
    position: absolute;
    background-color: rgb(186, 186, 186);
    padding: 2px;
    overflow: hidden;
    word-wrap: break-word;
}

.grid-cell-wrapper:hover {
    transform: scale(1.05);
    box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.9);
    background-color: rgb(255, 255, 255);
    z-index: 10;
}

.grid-cell {
    background-color: rgb(103, 103, 103);
    width: 100%;
    height: 100%;
    text-align: center;
    box-sizing: border-box;
}

.grid-header {
    width: 100%;
    height: 1px;
}

.grid-footer {
    position: absolute;
    background-color: rgb(186, 186, 186);
    left: 0px;
    height: 5px;
}

.label-details-card {
    width: 100%;
    height: 100%;
    padding: 0px;
}

.label-card-close-btn {
    cursor: pointer;
    position: absolute;
    color: rgb(185, 185, 185);
    top: 58px;
    right: 10px;
}

.image-region {
    width: 100%;
    height: 100%;
}

.tool-tag {
    margin: 8px 5px 0px 4px;
    cursor: default;
}

.close-btn {
    cursor: pointer;
    position: absolute;
    color: rgb(185, 185, 185);
    top: 10px;
    right: 10px;
}
</style>
